package com.agent.vpsserver.common.utils.http;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @desc httpclient请求工具
 */
@Slf4j
public class HttpClientPoolUtil {

    public static PoolingHttpClientConnectionManager cm = null;

    public volatile static CloseableHttpClient httpClient = null;

    public static SSLContext context = null;

    public static CloseableHttpClient httpsClient = null;

    /**
     * 默认content 类型
     */
    private static final String DEFAULT_CONTENT_TYPE = "application/json";

    /**
     * 默认请求超时时间30s
     */
    private static final int DEFAUL_TTIME_OUT = 30;

    private static final int COUNT = 100;

    private static final int TOTALCOUNT = 300;

    private static final int HTTP_DEFAULT_KEEP_TIME = 30;

    public static final String HTTP_TIMEOUT = "timeout";

    /**
     * 初始化连接池
     */
    public static void initPools() {
        if (httpClient == null) {
            synchronized (HttpClientPoolUtil.class) {
                cm = new PoolingHttpClientConnectionManager();
                cm.setDefaultMaxPerRoute(COUNT);
                cm.setMaxTotal(TOTALCOUNT);
                httpClient = HttpClients.custom().setKeepAliveStrategy(defaultStrategy).setConnectionManager(cm).build();
            }
        }
    }

    public static void initSSLPools() {
        try {
            if (httpsClient == null) {
                synchronized (HttpClientPoolUtil.class) {
                    context = createIgnoreVerifySSL();
                    // 设置协议http和https对应的处理socket链接工厂的对象
                    Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                            .register("http", PlainConnectionSocketFactory.INSTANCE)
                            .register("https", new SSLConnectionSocketFactory(context))
                            .build();
                    PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
                    HttpClients.custom().setConnectionManager(connManager);
                    httpsClient = HttpClients.custom().setConnectionManager(connManager).build();
                }
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
    }

    /**
     * 绕过验证
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sc = SSLContext.getInstance("SSLv3");
        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sc.init(null, new TrustManager[]{trustManager}, null);
        return sc;
    }

    /**
     * Http connection keepAlive 设置
     */
    public static ConnectionKeepAliveStrategy defaultStrategy = (response, context) -> {
        HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
        int keepTime = HTTP_DEFAULT_KEEP_TIME;
        while (it.hasNext()) {
            HeaderElement he = it.nextElement();
            String param = he.getName();
            String value = he.getValue();
            if (value != null && param.equalsIgnoreCase(HTTP_TIMEOUT)) {
                try {
                    return Long.parseLong(value) * 1000;
                } catch (Exception e) {
                    log.error("format KeepAlive timeout exception, exception:" + e.toString());
                }
            }
        }
        return keepTime * 1000;
    };

    public static CloseableHttpClient getHttpClient() {
        return httpClient;
    }

    public static PoolingHttpClientConnectionManager getHttpConnectionManager() {
        return cm;
    }

    /**
     * 执行http post请求 默认采用Content-Type：application/json，Accept：application/json
     *
     * @param uri  请求地址
     * @param data 请求数据
     * @return
     */
    public static String execute(Map<String,String> headerMap, String uri, String data, String referer) {
        return HttpClientPoolUtil.execute(headerMap, uri, data, referer, StandardCharsets.ISO_8859_1);
    }
    /**
     * 执行http post请求 默认采用Content-Type：application/json，Accept：application/json
     *
     * @param uri  请求地址
     * @param data 请求数据
     * @return
     */
    public static String execute(Map<String,String> headerMap, String uri, String data, String referer, Charset charsets) {
        long startTime = System.currentTimeMillis();
        HttpEntity httpEntity = null;
        HttpEntityEnclosingRequestBase method = null;
        String responseBody = "";
        try {
            if (httpClient == null) {
                initPools();
            }
            method = (HttpEntityEnclosingRequestBase) getRequest(uri, HttpPost.METHOD_NAME, DEFAULT_CONTENT_TYPE, 0);
            method.setEntity(new StringEntity(data, charsets));
            method.setHeader("Referer", referer);
            method.setHeader("Content-Type", "application/x-www-form-urlencoded");
            // 添加请求头
            if (headerMap != null && headerMap.size() > 0) {
                for (Map.Entry<String, String> entry : headerMap.entrySet()) {
                    BasicHeader basicHeader = new BasicHeader(entry.getKey(), entry.getValue());
                    method.setHeader(basicHeader);
                }
            }
            HttpContext context = HttpClientContext.create();
            CloseableHttpResponse httpResponse = httpClient.execute(method, context);
            httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                responseBody = EntityUtils.toString(httpEntity, "UTF-8");
            }

        } catch (Exception e) {
            if (method != null) {
                method.abort();
            }
            log.error("execute post request exception, url:" + uri + ", exception:" + e.toString()
                    + ", cost time(ms):" + (System.currentTimeMillis() - startTime));
        } finally {
            if (httpEntity != null) {
                try {
                    EntityUtils.consumeQuietly(httpEntity);
                } catch (Exception e) {
                    log.error("close response exception, url:" + uri + ", exception:" + e.toString()
                            + ", cost time(ms):" + (System.currentTimeMillis() - startTime));
                }
            }
        }
        return responseBody;
    }
    /**
     * 执行http post请求 默认采用Content-Type：application/json，Accept：application/json
     *
     * @param uri  请求地址
     * @param data 请求数据
     * @return
     */
    public static String execute(String uri, String data, String referer) {
        long startTime = System.currentTimeMillis();
        HttpEntity httpEntity = null;
        HttpEntityEnclosingRequestBase method = null;
        String responseBody = "";
        try {
            if (httpClient == null) {
                initPools();
            }
            method = (HttpEntityEnclosingRequestBase) getRequest(uri, HttpPost.METHOD_NAME, DEFAULT_CONTENT_TYPE, 0);
            method.setEntity(new StringEntity(data));
            method.setHeader("Referer", referer);
            method.setHeader("Content-Type", "application/x-www-form-urlencoded");
            HttpContext context = HttpClientContext.create();
            CloseableHttpResponse httpResponse = httpClient.execute(method, context);
            httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                responseBody = EntityUtils.toString(httpEntity, "UTF-8");
            }

        } catch (Exception e) {
            if (method != null) {
                method.abort();
            }
            log.error("execute post request exception, url:" + uri + ", exception:" + e.toString()
                    + ", cost time(ms):" + (System.currentTimeMillis() - startTime));
        } finally {
            if (httpEntity != null) {
                try {
                    EntityUtils.consumeQuietly(httpEntity);
                } catch (Exception e) {
                    log.error("close response exception, url:" + uri + ", exception:" + e.toString()
                            + ", cost time(ms):" + (System.currentTimeMillis() - startTime));
                }
            }
        }
        return responseBody;
    }

    /**
     * 执行post请求
     *
     * @param uri
     * @param data
     * @param referer
     * @param charset
     * @return
     */
    public static String execute(String uri, Map<String, String> data, String referer, String charset) {
        long startTime = System.currentTimeMillis();
        HttpEntity httpEntity = null;
        HttpEntityEnclosingRequestBase method = null;
        String responseBody = "";
        try {
            if (httpClient == null) {
                initPools();
            }
            String contentType = "application/x-www-form-urlencoded";
            method = (HttpEntityEnclosingRequestBase) getRequest(uri, HttpPost.METHOD_NAME, contentType, 0);

            // 设置参数
            List<NameValuePair> list = new ArrayList<NameValuePair>();
            Iterator iterator = data.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> elem = (Map.Entry<String, String>) iterator.next();
                list.add(new BasicNameValuePair(elem.getKey(), elem.getValue()));
            }
            if (list.size() > 0) {
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, charset);
                method.setEntity(entity);
            }
            method.setHeader("Referer", referer);
            HttpContext context = HttpClientContext.create();
            CloseableHttpResponse httpResponse = httpClient.execute(method, context);
            httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                responseBody = EntityUtils.toString(httpEntity, "UTF-8");
            }

        } catch (Exception e) {
            if (method != null) {
                method.abort();
            }
            log.error("execute post request exception, url:" + uri + ", exception:" + e.toString()
                    + ", cost time(ms):" + (System.currentTimeMillis() - startTime));
        } finally {
            if (httpEntity != null) {
                try {
                    EntityUtils.consumeQuietly(httpEntity);
                } catch (Exception e) {
                    log.error("close response exception, url:" + uri + ", exception:" + e.toString()
                            + ", cost time(ms):" + (System.currentTimeMillis() - startTime), e);
                }
            }
        }
        return responseBody;
    }

    /**
     * 发送post请求
     *
     * @param uri
     * @param json
     * @param referer
     * @param charset
     * @return
     */
    public static String execute(String uri, String json, String referer, String charset) {
        long startTime = System.currentTimeMillis();
        HttpEntity httpEntity = null;
        HttpEntityEnclosingRequestBase method = null;
        String responseBody = "";
        try {
            if (httpClient == null) {
                initPools();
            }
            method = (HttpEntityEnclosingRequestBase) getRequest(uri, HttpPost.METHOD_NAME, DEFAULT_CONTENT_TYPE, 0);

            method.setHeader("Referer", referer);
            method.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
            method.setHeader("Content-Type", DEFAULT_CONTENT_TYPE);
            HttpContext context = HttpClientContext.create();
            CloseableHttpResponse httpResponse = httpClient.execute(method, context);
            httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                responseBody = EntityUtils.toString(httpEntity, charset);
            }

        } catch (Exception e) {
            if (method != null) {
                method.abort();
            }
            log.error("execute post request exception, url:" + uri + ", exception:" + e.toString()
                    + ", cost time(ms):" + (System.currentTimeMillis() - startTime));
        } finally {
            if (httpEntity != null) {
                try {
                    EntityUtils.consumeQuietly(httpEntity);
                } catch (Exception e) {
                    log.error("close response exception, url:" + uri + ", exception:" + e.toString()
                            + ", cost time(ms):" + (System.currentTimeMillis() - startTime), e);
                }
            }
        }
        return responseBody;
    }

    public static String sendPost(String uri, String data) {
        HttpEntity httpEntity = null;
        HttpEntityEnclosingRequestBase method = null;
        String result = "";
        try {
            if (httpClient == null) {
                initSSLPools();
            }
            method = (HttpEntityEnclosingRequestBase) getRequest(uri, HttpPost.METHOD_NAME, DEFAULT_CONTENT_TYPE, 0);
            if (data != null) {
                method.setEntity(new StringEntity(data));
            }
            HttpContext context = HttpClientContext.create();
            CloseableHttpResponse httpResponse = httpClient.execute(method, context);
            httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                result = EntityUtils.toString(httpEntity, "UTF-8");
//				logger.info("发送POST请求返回结果："+result);
            }

        } catch (Exception e) {
            if (method != null) {
                method.abort();
            }
            e.printStackTrace();
        } finally {
            if (httpEntity != null) {
                try {
                    EntityUtils.consumeQuietly(httpEntity);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    public static String sendGet(String uri, Map<String, String> paramMap) {
        HttpEntity httpEntity = null;
        HttpGet method = null;
        String result = "";
        try {
            if (httpClient == null) {
                initSSLPools();
            }
            method = (HttpGet) getRequest(uri, HttpGet.METHOD_NAME, DEFAULT_CONTENT_TYPE, 0);
            HttpContext context = HttpClientContext.create();
            //创建URLBuilder
            URIBuilder uriBuilder=new URIBuilder(uri);
            //设置参数
            for (Map.Entry<String, String> param : paramMap.entrySet()) {
                uriBuilder.setParameter(param.getKey(), param.getValue());
            }
            method.setURI(uriBuilder.build());
            CloseableHttpResponse httpResponse = httpClient.execute(method, context);
            httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                result = EntityUtils.toString(httpEntity, "UTF-8");
                log.info("发送POST请求返回结果：" + result);
            }

        } catch (Exception e) {
            if (method != null) {
                method.abort();
            }
            e.printStackTrace();
        } finally {
            if (httpEntity != null) {
                try {
                    EntityUtils.consumeQuietly(httpEntity);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    public static String sendGet(String uri, Map<String, String> headerMap, Map<String, String> paramMap) {
        HttpEntity httpEntity = null;
        HttpGet method = null;
        String result = "";
        try {
            if (httpClient == null) {
                initSSLPools();
            }
            method = (HttpGet) getRequest(uri, HttpGet.METHOD_NAME, DEFAULT_CONTENT_TYPE, 0);
            // 添加请求头
            if (headerMap != null && headerMap.size() > 0) {
                for (Map.Entry<String, String> entry : headerMap.entrySet()) {
                    BasicHeader basicHeader = new BasicHeader(entry.getKey(), entry.getValue());
                    method.setHeader(basicHeader);
                }
            }
            HttpContext context = HttpClientContext.create();
            //创建URLBuilder
            URIBuilder uriBuilder=new URIBuilder(uri);
            //设置参数
            for (Map.Entry<String, String> param : paramMap.entrySet()) {
                uriBuilder.setParameter(param.getKey(), param.getValue());
            }
            method.setURI(uriBuilder.build());
            CloseableHttpResponse httpResponse = httpClient.execute(method, context);
            httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                result = EntityUtils.toString(httpEntity, "UTF-8");
                log.info("发送POST请求返回结果：" + result);
            }

        } catch (Exception e) {
            if (method != null) {
                method.abort();
            }
            e.printStackTrace();
        } finally {
            if (httpEntity != null) {
                try {
                    EntityUtils.consumeQuietly(httpEntity);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    /**
     * 创建请求
     *
     * @param uri         请求url
     * @param methodName  请求的方法类型
     * @param contentType contentType类型
     * @param timeout     超时时间
     * @return
     */
    public static HttpRequestBase getRequest(String uri, String methodName, String contentType, int timeout) {
        if (httpClient == null) {
            initPools();
        }
        HttpRequestBase method = null;
        if (timeout <= 0) {
            timeout = DEFAUL_TTIME_OUT;
        }
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout * 1000)
                .setConnectTimeout(timeout * 1000).setConnectionRequestTimeout(timeout * 1000)
                .setExpectContinueEnabled(false).build();

        method = method(methodName, uri);

        if (StringUtils.isBlank(contentType)) {
            contentType = DEFAULT_CONTENT_TYPE;
        }
        method.addHeader("Content-Type", contentType);
        method.addHeader("Accept", contentType);
        method.setConfig(requestConfig);
        return method;
    }

    public static HttpRequestBase getSSLRequest(String uri, String methodName, String contentType, int timeout) {
        if (httpsClient == null) {
            initSSLPools();
        }
        HttpRequestBase method = null;
        if (timeout <= 0) {
            timeout = DEFAUL_TTIME_OUT;
        }
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout * 1000)
                .setConnectTimeout(timeout * 1000).setConnectionRequestTimeout(timeout * 1000)
                .setExpectContinueEnabled(false).build();

        method = method(methodName, uri);

        if (StringUtils.isBlank(contentType)) {
            contentType = DEFAULT_CONTENT_TYPE;
        }
        method.addHeader("Content-Type", contentType);
        method.addHeader("Accept", contentType);
        method.addHeader("connection", "Keep-Alive");
        method.addHeader("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        method.setConfig(requestConfig);
        return method;
    }

    /**
     * 执行GET 请求
     *
     * @param uri
     * @return
     */
    public static String execute(String uri, String referer) {
        long startTime = System.currentTimeMillis();
        HttpEntity httpEntity = null;
        HttpRequestBase method = null;
        String responseBody = "";
        try {
            if (httpClient == null) {
                initPools();
            }
            method = getRequest(uri, HttpGet.METHOD_NAME, DEFAULT_CONTENT_TYPE, 0);
            method.setHeader("Referer", referer);
            HttpContext context = HttpClientContext.create();
            CloseableHttpResponse httpResponse = httpClient.execute(method, context);
            httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                responseBody = EntityUtils.toString(httpEntity, "UTF-8");
                log.error("请求URL: " + uri + "  返回状态码：" + httpResponse.getStatusLine().getStatusCode() + "  耗时：" + (System.currentTimeMillis() - startTime));
            }
        } catch (Exception e) {
            if (method != null) {
                method.abort();
            }
            log.error("execute get request exception, url:" + uri + ", exception:" + e.toString() + ",cost time(ms):"
                    + (System.currentTimeMillis() - startTime));
        } finally {
            if (httpEntity != null) {
                try {
                    EntityUtils.consumeQuietly(httpEntity);
                } catch (Exception e) {
                    log.error("close response exception, url:" + uri + ", exception:" + e.toString()
                            + ",cost time(ms):" + (System.currentTimeMillis() - startTime));
                }
            }
        }
        return responseBody;
    }

    /**
     * 检测视频地址是否有效
     *
     * @param url
     * @return
     */
    public static boolean getVedioUrlFilter(String url) {
        long startTime = System.currentTimeMillis();
        HttpRequestBase method = null;
        try {
            if (httpClient == null) {
                initPools();
            }
            method = getRequest(url, HttpGet.METHOD_NAME, DEFAULT_CONTENT_TYPE, 2);
            HttpContext context = HttpClientContext.create();
            CloseableHttpResponse httpResponse = httpClient.execute(method, context);
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK || statusCode < 400) {
                return true;
            } else {
                if (httpResponse.getFirstHeader(HTTP.CONTENT_TYPE).getValue().contains("application/json")) {
                    log.info("请求视频URL: " + url + "  返回状态码：" + statusCode + "，结果：" + EntityUtils.toString(httpResponse.getEntity(), "UTF-8"));
                }
            }
        } catch (Exception e) {
            if (method != null) {
                method.abort();
            }
            log.error("execute get request exception, url:" + url + ", exception:" + e.toString() + ",cost time(ms):"
                    + (System.currentTimeMillis() - startTime));
            // 请求失败的默认成功，失效的视频很快就可以请求成功
            return true;
        }
        return false;
    }

    /**
     * 执行获取请求方法
     *
     * @param methodName
     * @param uri
     * @return
     */
    private static HttpRequestBase method(String methodName, String uri) {
        HttpRequestBase method = null;
        if (HttpPut.METHOD_NAME.equalsIgnoreCase(methodName)) {
            method = new HttpPut(uri);
        } else if (HttpPost.METHOD_NAME.equalsIgnoreCase(methodName)) {
            method = new HttpPost(uri);
        } else if (HttpGet.METHOD_NAME.equalsIgnoreCase(methodName)) {
            method = new HttpGet(uri);
        } else {
            method = new HttpPost(uri);
        }
        return method;
    }

    public static String buildUrl(String url, Map<String, String> querys) throws UnsupportedEncodingException {
        StringBuilder sbUrl = new StringBuilder();
        sbUrl.append(url);
        if (null != querys) {
            StringBuilder sbQuery = new StringBuilder();
            for (Map.Entry<String, String> query : querys.entrySet()) {
                if (0 < sbQuery.length()) {
                    sbQuery.append("&");
                }
                if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
                    sbQuery.append(query.getValue());
                }
                if (!StringUtils.isBlank(query.getKey())) {
                    sbQuery.append(query.getKey());
                    if (!StringUtils.isBlank(query.getValue())) {
                        sbQuery.append("=");
                        sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8"));
                    }
                }
            }
            if (0 < sbQuery.length()) {
                sbUrl.append("?").append(sbQuery);
            }
        }

        return sbUrl.toString();
    }
}
